﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using XFControls.Utils;
using XFControls.XFPanels.XFPanelItems;

namespace XFControls.XFPanels
{
    public partial class XFPanelIcon : XFPanelBase, IEnumerable
    {
        public event ContextPopupEventHandler ContextPopup;
        public event ContextPopupEventHandler ContextClose;
        public event ContextPopupEventHandler BeforeContextPopup;
        public List<IXFItem> EmptyOptions = new List<IXFItem>();
        public bool PanelReceiveSwipe;
        public new ContextMenu ContextMenu
        {
            get { return m_context; }
            set { m_context = value; }
        }

        protected XFPanelList CurPanel;
        protected bool _itemSelect = true;
        protected XFPanelIconBottom _bottom;
        private Bitmap _buffer;
        private int _iconHeight;
        private int _iconWidth;
        protected List<IXFIcon> _items;
        protected int _page;
        protected int _totalPages;
        protected IXFIcon _selected;
        private ContextMenu m_context = new ContextMenu();
        private bool m_justDidContext;

        public XFPanelIcon()
        {
            InitializeComponent();
            _items = new List<IXFIcon>();
            _bottom = new XFPanelIconBottom();

            Height = Screen.PrimaryScreen.WorkingArea.Height;
            Width = Screen.PrimaryScreen.WorkingArea.Width;

            PanelReceiveSwipe = true;
            BackColor = Color.White;
            ContextMenu = m_context;

            var clear = new MenuItem { Text = "Clear" };
            clear.Click += clear_Click;
            m_context.MenuItems.Add(clear);
            m_context.Popup += (o, e) => selectIcon(MousePosition.X, MousePosition.Y);
        }

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        #endregion

        public event EventHandler<NumberEventArgs> EmptyClick;

        private void clear_Click(object sender, EventArgs e)
        {
            if (_selected == null) return;
            int numb = _selected.Number;
            IXFIcon temp = GenerateEmptyIcon(numb);
            Replace(temp, numb);
        }

        public void ClearItem(int item)
        {
            if (item >= _items.Count || item < 0)
                return;

            IXFIcon itm = _items[item];
            _items.RemoveAt(item);

            Reset();
        }

        public void ClearItems()
        {
            _items.Clear();
            Reset();
        }

        public void ClearItems(int start)
        {
            if (start >= _items.Count)
                return;

            _items.RemoveRange(start, _items.Count - start);
            Reset();
        }

        public void ClearItems(int start, int count)
        {
            _items.RemoveRange(start, count);
            Reset();
        }

        public override void DoSwipe(Point mouseDown, Point mouseUp)
        {
            if (!PanelReceiveSwipe) return;

            if ((mouseUp.X - mouseDown.X) > (Screen.PrimaryScreen.WorkingArea.Width / 3))
            {
                if (_page > 0)
                    _page--;

                if (_bottom.MagnifyingGlass && _page == 0)
                {
                    _curState = States.SlideRight;
                    SetLeftPanel(SearchPanel());
                }
            }
            else if ((mouseDown.X - mouseUp.X) > (Screen.PrimaryScreen.WorkingArea.Width / 3))
            {
                if (_page < _totalPages)
                    _page++;

                if (_bottom.Album && _page == _totalPages)
                {
                    _curState = States.SlideLeft;
                    SetRightPanel(AlbumPanel());
                }
            }

            reset();
            Invalidate();
        }

        public void Add(IXFIcon item)
        {
            Add(item, _items.Count + 1);
        }

        public void Add(IXFIcon item, int index)
        {
            item.Height = _iconHeight;
            item.Width = _iconWidth;

            item.Parent = this;
            if (index > _items.Count)
                _items.Add(item);
            else
                _items.Insert(index, item);
        }

        public void Replace(IXFIcon item, int index)
        {
            item.Height = _iconHeight;
            item.Width = _iconWidth;

            item.Parent = this;
            if (index > _items.Count)
                _items.Add(item);
            else
            {
                _items.Insert(index, item);
                _items.RemoveAt(index + 1);
            }

            reset();
        }

        public override void PanelPaint(Graphics g)
        {
            if (_buffer != null)
            {
                g.DrawImage(_buffer, 0, 0);
                drawSelected(g);
                return;
            }

            if (_iconHeight <= 0 || _iconWidth <= 0)
            {
                Height = Parent != null ? Parent.Height : Screen.PrimaryScreen.WorkingArea.Height;

                _iconWidth = Width / 3;
                _iconHeight = (Height - (Height >> 3)) / 3;
            }

            _buffer = XFBitmapFactory.GetBitmap(Width, Height);
            int tempPage = _bottom.MagnifyingGlass ? _page - 1 : _page;

            if (tempPage == -1)
                DrawSearch();
            else if (_page == _totalPages - 1)
                DrawAlbum();
            else
                DrawIcons(tempPage);

            PanelPaint(g);
        }

        private void DrawAlbum()
        {

            if (CurPanel == null)
                CurPanel = AlbumPanel();
            else
            {
                if (Parent.Controls.Contains(CurPanel))
                    Parent.Controls.Remove(CurPanel);
                CurPanel.Dispose();

                CurPanel = AlbumPanel();
            }

            Parent.Controls.Add(CurPanel);
            _itemSelect = false;
            using (Graphics gb = Graphics.FromImage(_buffer))
            {
                gb.Clear(BackColor);
                CurPanel.PanelPaint(gb);
                _bottom.Draw(gb, _iconHeight * 3, (Height >> 3), _page, _totalPages);
            }
        }

        private void DrawSearch()
        {
            if (CurPanel == null)
                CurPanel = SearchPanel();
            else
            {
                if (Parent.Controls.Contains(CurPanel))
                    Parent.Controls.Remove(CurPanel);
                CurPanel.Dispose();

                CurPanel = SearchPanel();
            }

            Parent.Controls.Add(CurPanel);
            _itemSelect = false;
            using (Graphics gb = Graphics.FromImage(_buffer))
            {
                gb.Clear(BackColor);
                CurPanel.PanelPaint(gb);
                _bottom.Draw(gb, _iconHeight * 3, (Height >> 3), _page, _totalPages);
            }
        }

        protected virtual XFPanelList AlbumPanel()
        {
            throw new NotImplementedException();
        }

        protected virtual XFPanelList SearchPanel()
        {
            throw new NotImplementedException();
        }

        private void DrawIcons(int tempPage)
        {
            if (CurPanel != null)
            {
                if (Parent.Controls.Contains(CurPanel))
                    Parent.Controls.Remove(CurPanel);
                CurPanel.Dispose();

                CurPanel = null;
            }

            int cur = 0;
            _itemSelect = true;
            using (Graphics gb = Graphics.FromImage(_buffer))
            {
                gb.Clear(BackColor);
                for (int i = (tempPage * 9); i < _items.Count && cur < 9; i++)
                {
                    int xNumb = cur % 3;
                    int yNumb = cur / 3;

                    if (_items[i].Width == 0 || _items[i].Height == 0)
                    {
                        _items[i].Width = _iconWidth;
                        _items[i].Height = _iconHeight;
                    }
                    _items[i].PaintIcon(gb, xNumb * _iconWidth, yNumb * _iconHeight);
                    cur++;
                }

                for (int i = cur; i < 9; i++)
                {
                    IXFIcon temp = GenerateEmptyIcon(cur + (tempPage * 9));
                    Add(temp);

                    int xNumb = cur % 3;
                    int yNumb = cur / 3;

                    temp.PaintIcon(gb, xNumb * _iconWidth, yNumb * _iconHeight);
                    cur++;
                }

                _bottom.Draw(gb, _iconHeight * 3, (Height >> 3), _page, _totalPages);
            }
        }

        protected IXFIcon GenerateEmptyIcon(int cur)
        {
            string empty = "Empty";
            var temp = new XFIcon(empty, "plus") { Width = _iconWidth, Height = _iconHeight, Number = cur, IsEmpty = true };

            temp.Click += EmptyClickMethod;

            return temp;
        }

        private void EmptyClickMethod(object sender, EventArgs args)
        {
            int numb = ((IXFIcon)sender).Number;

            if (EmptyClick != null)
                EmptyClick(sender, new NumberEventArgs(numb));

            var icon = sender as IXFIcon;
            if (icon.Parent == null)
                return;

            XFPanelIcon panel = icon.Parent;

            panel.SetRightPanel(IconSelectPanel(numb));
            panel.EnableSlide();
        }

        private XFPanelBase IconSelectPanel(int number)
        {
            var ret = new XFPanelList();

            var back = new XFItemBackHeader();
            ret.Add(back);

            foreach (IXFItem opt in EmptyOptions)
            {
                var optCpy = ret.AddCopy(opt);
                CopyItems(optCpy, number);
            }

            return ret;
        }

        private void CopyItems(IXFItem optCpy, int number)
        {
            if (optCpy is IXFItemContainer)
            {
                foreach (var item in ((IXFItemContainer)optCpy).GetItems())
                {
                    CopyItems(item, number);
                }
            }

            if (!(optCpy.Tag is IXFIcon))
            {
                if (optCpy.Tag == null)
                    optCpy.Tag = number;
                return;
            }

            optCpy.Click += ItemClick;
            ((IXFIcon)optCpy.Tag).Number = number;
        }

        protected void ItemClick(object o, EventArgs e)
        {
            var sender = (IXFItem)o;
            var ret = (XFPanelList)sender.Parent;
            int numb = 0;
            if (_selected != null)
                numb = _selected.Number;

            if (ret == null) return;
            var icons = ret.LeftPanel as XFPanelIcon;

            if (sender.Tag == null) return;

            if (icons == null)
            {
                if (ret.LeftPanel == null || ret.LeftPanel.LeftPanel == null) return;

                icons = ret.LeftPanel.LeftPanel as XFPanelIcon;
                if (icons == null) return;

                ret.SetLeftPanel(ret.LeftPanel.LeftPanel);
            }
            var icon = ((IXFIcon)sender.Tag).GetCopy();
            icons.Replace(icon, icon.Number);
            ret.EnableSlide();

            SaveState();
        }

        private void drawSelected(Graphics g)
        {
            if (_selected != null)
                g.DrawRoundedBorder(Color.Blue, _selected.ImageRectange, _selected.ImageRectange.Height >> 2, 5, XFGraphicExtensions.RoundedCorners.All);
        }

        private void reset()
        {
            if (_buffer != null)
                _buffer.Dispose();
            _buffer = null;
        }

        public void OnBlur()
        {
            if (_selected != null)
            {
                _selected.Selected = false;
                Invalidate();
            }
            _selected = null;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (!_itemSelect && CurPanel != null)
                CurPanel.MouseDown(e);
            else
            {
                switch (_curState)
                {
                    case States.Click:
                        selectIcon(e.X, e.Y);
                        break;
                    default:
                        OnBlur();
                        break;
                }
                CheckContext(e, this, Handle);
            }
        }

        protected void CheckContext(MouseEventArgs e, Control control, IntPtr hwnd)
        {
            //
            // Tap and Hold: This overrides the built-in implementation to
            // allow you to receive the MouseDown event before the context
            // menu is displayed.
            //
            m_justDidContext = false;

            if (m_context != null)
            {
                //
                // Give a chance to cancel the context menu (or change the menu that we'll show).
                //
                ContextPopupEventArgs eContext = new ContextPopupEventArgs(e.X, e.Y);
                OnBeforeContextPopup(eContext);		// Give subclass a chance to cancel or change context

                bool fShow = false;

                if (eContext.Cancel == false)
                    fShow = RecognizeGesture(hwnd, e.X, e.Y);

                if (fShow)
                    ShowContext(control, eContext);
            }
        }

        //
        // ShowContext
        //
        protected void ShowContext(Control control, ContextPopupEventArgs eContext)
        {
            if (m_context != null)
            {
                m_justDidContext = true;

                if (_selected != null && !_selected.IsEmpty)
                {
                    OnContextPopup(eContext);
                    m_context.Show(control, new Point(eContext.X, eContext.Y));
                    OnContextClose(eContext);
                }
            }
        }

        //
        // CheckContext
        //
        protected bool RecognizeGesture(IntPtr hwnd, int x, int y)
        {
            SHRGINFO info = new SHRGINFO();
            info.cbSize = Marshal.SizeOf(info);
            info.ptDown = new Point(x, y);
            info.dwFlags = SHRG_RETURNCMD;
            info.hwndClient = hwnd;

            int cmd = SHRecognizeGesture(ref info);

            return (cmd == GN_CONTEXTMENU);
        }

        protected void OnBeforeContextPopup(ContextPopupEventArgs e)
        {
            if (BeforeContextPopup != null)
                BeforeContextPopup(this, e);
        }

        protected void OnContextClose(ContextPopupEventArgs e)
        {
            if (ContextClose != null)
                ContextClose(this, e);
        }

        protected virtual void OnContextPopup(ContextPopupEventArgs e)
        {
            if (ContextPopup != null)
                ContextPopup(this, e);
        }

        private void selectIcon(int x, int y)
        {
            int tempPage = _bottom.MagnifyingGlass ? _page - 1 : _page;
            int numb = tempPage * 9;
            for (int i = 0; i < 9; i++)
            {
                int xNumb = i % 3;
                int yNumb = i / 3;

                if (xNumb * _iconWidth >= x || (xNumb + 1) * _iconWidth <= x || yNumb * _iconHeight >= y || (yNumb + 1) * _iconHeight <= y) continue;

                _selected = _items[i + numb];
                _selected.Number = i + numb;
                _selected.Selected = true;

                Invalidate();
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (!_itemSelect && CurPanel != null)
                CurPanel.MouseUp(e);
            else
            {

                switch (_curState)
                {
                    case States.Click:
                        if (_selected != null)
                            _selected.OnClick();
                        OnBlur();
                        break;
                }
            }
            base.OnMouseUp(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (!_itemSelect && CurPanel != null)
                CurPanel.MouseMove(e);
            else
            {

                switch (_curState)
                {
                    case States.Scroll:
                        OnBlur();
                        Invalidate();
                        break;
                    case States.Swipe:
                        OnBlur();
                        Invalidate();
                        break;
                }
            }
        }

        protected override void Dispose()
        {
            ClearItems();
            if (_buffer != null)
                _buffer.Dispose();

            base.Dispose();
        }

        protected virtual void SaveState() { }

        [DllImport("aygshell.dll")]
        private static extern int SHRecognizeGesture(ref SHRGINFO info);

        private const int SHRG_RETURNCMD = 0x1;
        private const int GN_CONTEXTMENU = 1000;

        private struct SHRGINFO
        {
            public int cbSize;
            public IntPtr hwndClient;
            public Point ptDown;
            public int dwFlags;
        }
    }

    public delegate void ContextPopupEventHandler(object sender, ContextPopupEventArgs e);

    public class ContextPopupEventArgs : EventArgs
    {
        private int m_x, m_y;
        public bool Cancel;

        public ContextPopupEventArgs(int x, int y)
        {
            m_x = x;
            m_y = y;
            Cancel = false;
        }

        public int X
        {
            get { return m_x; }
        }

        public int Y
        {
            get { return m_y; }
        }
    }

    public class NumberEventArgs : EventArgs
    {
        public int Number;

        public NumberEventArgs(int number)
        {
            Number = number;
        }
    }
}